// <copyright file="TreeView.cs" company="YUIAsp.Net">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>raul popescu</author>
// <email>dev@yuiasp.net</email>
// <date>2009-01-30</date>
// <summary>YUIAsp.Net File</summary>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

[assembly: WebResource("YUIAspNet.TreeView.TreeView.js", "text/javascript")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.DefaultSkin.TreeView.treeview-loading.gif", "image/gif")]

#region DefaultSkin
[assembly: WebResource("YUIAspNet.EmbeddedSkins.DefaultSkin.TreeView.treeview.css", "text/css", PerformSubstitution = true)]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.DefaultSkin.TreeView.treeview-sprite.gif", "image/gif")] 
#endregion

#region Arrow
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Arrow.TreeView.treeview.css", "text/css", PerformSubstitution = true)]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Arrow.TreeView.sprite-menu.gif", "image/gif")]
#endregion

#region Folder
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.treeview.css", "text/css", PerformSubstitution = true)]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.lm.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.lmh.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.ln.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.lp.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.lph.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.tm.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.tmh.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.tn.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.tp.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.tph.gif", "image/gif")]
[assembly: WebResource("YUIAspNet.EmbeddedSkins.Folder.TreeView.vline.gif", "image/gif")] 
#endregion

namespace YUIAspNet
{
    /// <summary>
    /// TreeView control.
    /// </summary>
    [ToolboxItem(true)]
    [ParseChildren(true)]
    public class TreeView : Common.YUIScriptBaseControl, INamingContainer, IPostBackDataHandler
    {
        private Panel _treeContainer;
        private string expandTo;
        private IList<TreeNode> _nodes;

        /// <summary>
        /// Occurs when a <see cref="TreeNode"/> is clicked.
        /// </summary>
        public event EventHandler<TreeViewClickEventArgs> Click;


        /// <summary>
        /// Initializes a new instance of the <see cref="TreeView"/> class.
        /// </summary>
        public TreeView()
        {
            PreRender += TreeView_PreRender;

            _treeContainer = new Panel();
        }

        /// <summary>
        /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
        /// </summary>
        protected override void CreateChildControls()
        {
            _treeContainer.ID = "treeContainer";
            Controls.Add(_treeContainer);
            
            base.CreateChildControls();
        }

        void TreeView_PreRender(object sender, EventArgs e)
        {
            if (Page != null)
                Page.RegisterRequiresPostBack(this);

            if (CreateMarkup && Nodes != null && Nodes.Count > 0)
            {
                // create nodes as anchors, to make the tree SEO friendly...
                StringBuilder nodeMarkup = new StringBuilder();
                nodeMarkup.Append("<ul>");
                foreach (TreeNode node in Nodes)
                {
                    CreateMarkupNode(node, nodeMarkup);
                }
                nodeMarkup.Append("</ul>");

                _treeContainer.Controls.Add(new LiteralControl(nodeMarkup.ToString()));
            }
        }

        /// <summary>
        /// Registers the style sheets for the current control.
        /// Must be overriden in the parent control to add any css file reference.
        /// </summary>
        protected override void RegisterStyleSheets()
        {
            RegisterEmbeddedStylesheet("TreeView", "treeview.css");
        }

        /// <summary>
        /// Register the values to send to the client control for initialization.
        /// </summary>
        protected override void RegisterClientInitialization()
        {
            AddProperty("TreeContainer", _treeContainer.ClientID);
            if (!CreateMarkup)
               AddProperty("Nodes", Nodes);

            AddProperty("LazyLoad", LazyLoad);
            AddProperty("AutoPostback", AutoPostback);
            if(!string.IsNullOrEmpty(expandTo))
                AddProperty("ExpandTo", expandTo);

            if(!string.IsNullOrEmpty(OnClientNodeClick))
                AddEvent("OnNodeClick", OnClientNodeClick);

            if (!string.IsNullOrEmpty(OnClientNodeExpand))
                AddEvent("OnNodeExpand", OnClientNodeExpand);

            if (!string.IsNullOrEmpty(OnClientNodeCollapse))
                AddEvent("OnNodeCollapse", OnClientNodeCollapse);

            if (!string.IsNullOrEmpty(OnClientNodePopulate))
                AddEvent("OnNodePopulate", OnClientNodePopulate);
        }

        /// <summary>
        /// Creates the markup for a tree node.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="nodeMarkup">The node markup.</param>
        private static void CreateMarkupNode(TreeNode node, StringBuilder nodeMarkup)
        {
            if (node.NavigateUrl != null)
                nodeMarkup.AppendFormat(CultureInfo.InvariantCulture, "<li><a href='{0}'>{1}</a>", node.NavigateUrl.OriginalString, node.Text);
            else
                nodeMarkup.AppendFormat(CultureInfo.InvariantCulture, "<li><a href='#'>{0}</a>", node.Text);

            if (node.Nodes != null && node.Nodes.Count > 0)
            {
                nodeMarkup.Append("<ul>");

                foreach (TreeNode childNode in node.Nodes)
                    CreateMarkupNode(childNode, nodeMarkup);

                nodeMarkup.Append("</ul>");
            }

            nodeMarkup.Append("</li>");

        }

        /// <summary>
        /// Registers any additional scripts to include for this control.
        /// </summary>
        protected override void RegisterScriptReferences()
        {
            AddScriptReference(new ScriptReference("YUIAspNet.TreeView.TreeView.js", GetType().Assembly.FullName));
        }

        #region Public properties
        /// <summary>
        /// Gets or sets the nodes.
        /// </summary>
        /// <value>The nodes.</value>
        [Category("Data"), Description("Node options.")]
        [DefaultValue((string)null), PersistenceMode(PersistenceMode.InnerProperty), MergableProperty(false), NotifyParentProperty(true)]
        public IList<TreeNode> Nodes 
        { 
            get
            {
                if(_nodes == null)
                    _nodes = new List<TreeNode>();
                return _nodes;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the tree is lazy loading.
        /// </summary>
        /// <value><c>true</c> to lazy load the tree; otherwise, <c>false</c>.</value>
        public bool LazyLoad { get; set; }

        /// <summary>
        /// Gets or sets the on client node click javascript handler function.
        /// </summary>
        /// <value>The on client node click.</value>
        public string OnClientNodeClick { get; set; }

        /// <summary>
        /// Gets or sets the on client node expand javascript handler function.
        /// </summary>
        /// <value>The on client node expand.</value>
        public string OnClientNodeExpand { get; set; }

        /// <summary>
        /// Gets or sets the on client node collapse javascript handler function.
        /// </summary>
        /// <value>The on client node collapse.</value>
        public string OnClientNodeCollapse { get; set; }

        /// <summary>
        /// Gets or sets the on client node populate javascript handler function.
        /// </summary>
        /// <value>The on client node populate.</value>
        public string OnClientNodePopulate { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether auto postback is enabled.
        /// </summary>
        /// <value><c>true</c> if auto postback is enabled; otherwise, <c>false</c>.</value>
        public bool AutoPostback { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether to also create markup for the control.
        /// </summary>
        /// <value><c>true</c> if [create markup]; otherwise, <c>false</c>.</value>
        public bool CreateMarkup { get; set; }
        #endregion

        #region IPostBackDataHandler Members

        /// <summary>
        /// When implemented by a class, processes postback data for an ASP.NET server control.
        /// </summary>
        /// <param name="postDataKey">The key identifier for the control.</param>
        /// <param name="postCollection">The collection of all incoming name values.</param>
        /// <returns>
        /// true if the server control's state changes as a result of the postback; otherwise, false.
        /// </returns>
        public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
        {
            if (postCollection["__EVENTTARGET"] == ClientID)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// When implemented by a class, signals the server control to notify the ASP.NET application that the state of the control has changed.
        /// </summary>
        public void RaisePostDataChangedEvent()
        {
            if (Click != null)
                Click(this, new TreeViewClickEventArgs(Page.Request.Form["__EVENTARGUMENT"]));

            expandTo = Page.Request.Form["__EVENTARGUMENT"];
        }
        #endregion
    }

    /// <summary>
    /// Event args for TreeNode click.
    /// </summary>
    public class TreeViewClickEventArgs : EventArgs
    {
        /// <summary>
        /// Gets or sets the node.
        /// </summary>
        /// <value>The node.</value>
        public string Node { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="TreeViewClickEventArgs"/> class.
        /// </summary>
        /// <param name="node">The node.</param>
        public TreeViewClickEventArgs(string node)
        {
            Node = node;
        }
    }
}